package com.easylinkin.linkappapi.ruleengine.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.common.utils.DateUtil;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.service.DeviceService;
import com.easylinkin.linkappapi.intelligentrule.entity.IntelligentRuleExpression;
import com.easylinkin.linkappapi.intelligentrule.service.IntelligentRuleExpressionService;
import com.easylinkin.linkappapi.ruleengine.constant.RuleTriggerConstant;
import com.easylinkin.linkappapi.ruleengine.entity.RuleEngine;
import com.easylinkin.linkappapi.ruleengine.entity.RuleTrigger;
import com.easylinkin.linkappapi.ruleengine.entity.RuleTriggerRefDevice;
import com.easylinkin.linkappapi.ruleengine.mapper.RuleTriggerMapper;
import com.easylinkin.linkappapi.ruleengine.service.RuleTriggerRefDeviceService;
import com.easylinkin.linkappapi.ruleengine.service.RuleTriggerService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.taskscheduler.constant.JobEntityConstant.IsNowRunEnum;
import com.easylinkin.linkappapi.taskscheduler.constant.JobEntityConstant.JobStatusEnum;
import com.easylinkin.linkappapi.taskscheduler.constant.JobEntityConstant.JobTaskTypeEnum;
import com.easylinkin.linkappapi.taskscheduler.constant.JobEntityConstant.JobTypeEnum;
import com.easylinkin.linkappapi.taskscheduler.entity.JobEntity;
import com.easylinkin.linkappapi.taskscheduler.service.JobEntityService;
import com.easylinkin.linkappapi.taskscheduler.service.TaskSchedulerService;

import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

/**
 * <p>
 * 规则触发器 服务实现类
 * </p>
 *
 * @author TongJie
 * @since 2020-08-24
 */
@Service
public class RuleTriggerServiceImpl extends ServiceImpl<RuleTriggerMapper, RuleTrigger> implements RuleTriggerService {

    private static final Logger LOGGER = LoggerFactory.getLogger(RuleTriggerServiceImpl.class);

    @Resource
    private LinkappUserContextProducer linkappUserContextProducer;
    @Resource
    private TaskSchedulerService taskSchedulerService;
    @Resource
    private JobEntityService jobEntityService;
    @Resource
    private DeviceService deviceService;
    @Resource
    private IntelligentRuleExpressionService intelligentRuleExpressionService;
    @Resource
    private RuleTriggerRefDeviceService ruleTriggerRefDeviceService;


    @Override
    public List<RuleTrigger> getRuleTriggers(RuleTrigger ruleTrigger) {
        return baseMapper.getRuleTriggers(ruleTrigger);
    }

    @Override
    public IPage<RuleTrigger> getRuleTriggers(Page page, RuleTrigger ruleTrigger) {
        return baseMapper.getRuleTriggers(page, ruleTrigger);
    }

    @Override
    public RuleTrigger getRuleTrigger(String id) {
        RuleTrigger record = baseMapper.getRuleTrigger(id);
        return record;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(RuleTrigger ruleTrigger) {
        validParamRequired(ruleTrigger);
        validParamFormat(ruleTrigger);
        validRepeat(ruleTrigger);
        Date now = new Date();
        ruleTrigger.setCreateTime(now);
        ruleTrigger.setModifyTime(now);
        ruleTrigger.setSortFlag(DateUtil.getyyyyMMddHHmmssSSSNumberString(now));

        save(ruleTrigger);

        if (RuleTriggerConstant.TRIGGER_TYPE_CRON.equals(ruleTrigger.getType())) {
            saveJobAndUpdateRuleTrigger(ruleTrigger);
        } else {
            //如果是 设备状态类型的则新增与设备关联
            List<RuleTriggerRefDevice> ruleTriggerRefDeviceList = ruleTrigger.getRuleTriggerRefDeviceList();
            Assert.notEmpty(ruleTriggerRefDeviceList, "新增时触发器为type1 或type 2 关联设备列表不能为空");
            for (RuleTriggerRefDevice ruleTriggerRefDevice : ruleTriggerRefDeviceList) {
                ruleTriggerRefDevice.setRuleTriggerId(ruleTrigger.getId());
                ruleTriggerRefDeviceService.save(ruleTriggerRefDevice);
            }
        }

        if (RuleTriggerConstant.TRIGGER_TYPE_DEVICE_STATUS.equals(ruleTrigger.getType())) {
            List<IntelligentRuleExpression> intelligentRuleExpressions = ruleTrigger.getIntelligentRuleExpressionList();
            Assert.notEmpty(intelligentRuleExpressions, "触发器type为1时，intelligentRuleExpressionList不能为空！");
            if (ObjectUtils.isNotEmpty(intelligentRuleExpressions)) {
                for (IntelligentRuleExpression intelligentRuleExpression : intelligentRuleExpressions) {
                    intelligentRuleExpression.setRuleTriggerId(ruleTrigger.getId());
                    intelligentRuleExpressionService.save(intelligentRuleExpression);
                }
            }
        }
    }

    public void saveJobAndUpdateRuleTrigger(RuleTrigger ruleTrigger) {
        JobEntity jobEntity = ruleTrigger.getJobEntity();
        Assert.notNull(jobEntity, "触发器类型为定时任务类型，jobEntity 不能为空");
        setJobEntityDefaultProperty(jobEntity);
//        尽量使得任务名称唯一 以防根据名称执行 会执行到多个任务
        jobEntity.setJobName(ruleTrigger.getId());

        jobEntity.setJobGroup(generateJobGroupName(jobEntity.getJobGroup(), linkappUserContextProducer.getNotNullCurrent().getTenantId()));

        Date date = new Date();
        jobEntity.setCreateTime(date);
        jobEntity.setModifyTime(date);
        jobEntity.setJobClassName(RuleEngineServiceImpl.class.getName());

        taskSchedulerService.saveTask(jobEntity);

        ruleTrigger.setJobEntityId(jobEntity.getId());
        updateById(ruleTrigger);
    }

    /**
     * 如果某些属性为空 给设置默认值
     */
    private void setJobEntityDefaultProperty(JobEntity jobEntity) {
        if (StringUtils.isEmpty(jobEntity.getJobStatus())) {
            jobEntity.setJobStatus(JobStatusEnum.ON.getType());
        }
        if (StringUtils.isEmpty(jobEntity.getJobTaskType())) {
            jobEntity.setJobTaskType(JobTaskTypeEnum.API.getType());
        }
        if (StringUtils.isEmpty(jobEntity.getJobType())) {
            jobEntity.setJobType(JobTypeEnum.CYCLE.getType());
        }
        if (StringUtils.isEmpty(jobEntity.getIsNowRun())) {
            jobEntity.setIsNowRun(IsNowRunEnum.NO.getType());
        }
    }

    private String generateJobGroupName(String jobGroup, String tenantId) {
        if (ObjectUtils.isEmpty(jobGroup)) {
            return tenantId;
        }
        return jobGroup + "_" + tenantId;
    }

    /**
     * 校验重复
     */
    private void validRepeat(RuleTrigger ruleTrigger) {
    }


    /**
     * 校验参数必填
     */
    private void validParamRequired(RuleTrigger ruleTrigger) {
        Assert.notNull(ruleTrigger, "参数为空");
        Assert.notNull(ruleTrigger.getType(), "参数中条件类型为空");
        Assert.isTrue(StringUtils.isNotBlank(ruleTrigger.getRuleEngineId()), "getRuleEngineId 为空");
    }

    /**
     * 校验参数格式
     */
    private void validParamFormat(RuleTrigger ruleTrigger) {
        Assert.isTrue(ruleTrigger.getSortFlag() == null || ruleTrigger.getSortFlag().length() <= 20, "getSortFlag 超长");
        Assert.isTrue(ruleTrigger.getRuleEngineId() == null || ruleTrigger.getRuleEngineId().length() <= 32, "getRuleEngineId 超长");
        if (ruleTrigger.getJobEntity() != null) {
            JobEntity jobEntity = ruleTrigger.getJobEntity();
            if (jobEntity != null) {
                Assert.isTrue(jobEntity.getCronExpression() == null || jobEntity.getCronExpression().length() <= 120, "cron表达式 超长");
            }
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(List<RuleTrigger> ruleTriggerList) {
        if (ObjectUtils.isEmpty(ruleTriggerList)) {
            return;
        }
        List<String> ids = ruleTriggerList.stream().map(RuleTrigger::getId).filter(id -> StringUtils.isNotEmpty(id)).collect(Collectors.toList());
        if (ObjectUtils.isEmpty(ids)) {
            return;
        }
//        删除与之关联的规则表达式
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.in("rule_trigger_id", ids);
        intelligentRuleExpressionService.remove(queryWrapper);
        ruleTriggerRefDeviceService.remove(queryWrapper);
        List<String> jobEntityIds = listByIds(ids).stream().map(RuleTrigger::getJobEntityId).collect(Collectors.toList());
        Collection<JobEntity> jobEntityList = jobEntityService.listByIds(jobEntityIds);
        for (JobEntity jobEntity : jobEntityList) {
            taskSchedulerService.remove(jobEntity);
        }
        removeByIds(ids);
    }

    @Override
    public List<RuleTrigger> getRuleTriggersAndExpressions(RuleTrigger ruleTrigger) {
        return baseMapper.getRuleTriggersAndExpressions(ruleTrigger);
    }


    @Override
    public void disableByTenantId(String tenantId) {

        Assert.hasLength(tenantId, "租户id 为空");
        QueryWrapper<RuleTrigger> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("tenant_id", tenantId);
        queryWrapper.select("job_entity_id");

        List<RuleTrigger> ruleTriggerList = list(queryWrapper);
        List<String> jobEntityIds = ruleTriggerList.stream().map(RuleTrigger::getJobEntityId).collect(Collectors.toList());
        if (ObjectUtils.isEmpty(jobEntityIds)) {
            return;
        }

        QueryWrapper<JobEntity> qw = new QueryWrapper();
        qw.in("id", jobEntityIds);
        qw.eq("job_status", JobStatusEnum.ON.getType());
        List<JobEntity> jobEntities = jobEntityService.list(qw);

        for (JobEntity jobEntity : jobEntities) {
            jobEntity.setJobStatus(JobStatusEnum.OFF.getType());
            taskSchedulerService.updateTask(jobEntity);
        }
    }

    @Override
    public void updateStatusForType3ByRuleEngineChange(RuleEngine ruleEngine) {

        Assert.notNull(ruleEngine,"参数为空");
        Assert.hasLength(ruleEngine.getId(),"参数 id 为空");
        Assert.notNull(ruleEngine.getStatus(),"参数 status 为空");

        QueryWrapper<RuleTrigger> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("rule_engine_id", ruleEngine.getId());
        queryWrapper.eq("type", RuleTriggerConstant.TRIGGER_TYPE_CRON);
        queryWrapper.select("job_entity_id");

        List<RuleTrigger> ruleTriggerList = list(queryWrapper);
        List<String> jobEntityIds = ruleTriggerList.stream().map(RuleTrigger::getJobEntityId).collect(Collectors.toList());
        if (ObjectUtils.isEmpty(jobEntityIds)) {
            return;
        }

        QueryWrapper<JobEntity> qw = new QueryWrapper();
        qw.in("id", jobEntityIds);
//        qw.eq("job_status", JobStatusEnum.ON.getType());
        qw.ne("job_status", ruleEngine.getStatus());
        List<JobEntity> jobEntities = jobEntityService.list(qw);

        for (JobEntity jobEntity : jobEntities) {
            jobEntity.setJobStatus(String.valueOf(ruleEngine.getStatus()));
            taskSchedulerService.updateTask(jobEntity);
        }
    }

    @Override
    public Set<String> getMeetRuleTriggerRuleEngineIds(Device device, Object data) {

        Set<String> meetRuleTriggerRuleEngineIds = new HashSet<>();

//        Assert.notNull(device, "参数(设备)为空");
        if (device == null) {
            LOGGER.warn("getMeetRuleTriggerRuleEngineIds-设备参数为空");
            return meetRuleTriggerRuleEngineIds;
        }
        Assert.hasLength(device.getCode(), "设备code 为空");
        Device originDevice = deviceService.findOneByDeviceCode(device.getCode());
//        Assert.notNull(originDevice, "根据设备code查找不到设备，检查是否被删,code:" + device.getCode());
        if (originDevice == null) {
            LOGGER.warn("根据设备code查找不到设备，检查是否被删,code:{}", device.getCode());
            return meetRuleTriggerRuleEngineIds;
        }
        device.setDeviceUnitId(originDevice.getDeviceUnitId());
        LOGGER.info("判断是否应产生告警，参数:{}", JSONObject.toJSONString(device));

        List<RuleTrigger> ruleTriggers = baseMapper.getRuleTriggersByRelatedDeviceCode(device.getCode(),originDevice.getTenantId());

        for (RuleTrigger ruleTrigger : ruleTriggers) {
            if (!meetRuleTriggerRuleEngineIds.contains(ruleTrigger.getRuleEngineId()) && checkMeetRuleTrigger(device, ruleTrigger, data)) {
                meetRuleTriggerRuleEngineIds.add(ruleTrigger.getRuleEngineId());
            }
        }

        return meetRuleTriggerRuleEngineIds;
    }


    @Override
    public boolean checkMeetRuleTrigger(Device device, RuleTrigger ruleTrigger, Object data) {
//        如果是上下线推送
        if ("onlineStateChange".equals(data)) {
            //        如果是常规规则 只需要判断 设备在线离线是否符合
            if (RuleTriggerConstant.TRIGGER_TYPE_DEVICE_ON_OFF_LINE.equals(ruleTrigger.getType())) {
                // 当设备状态不满足 智能规则所设定值
                if (ObjectUtils.isNotEmpty(ruleTrigger.getCommonRuleItem()) && ruleTrigger.getCommonRuleItem().equals(device.getOnlineState())) {
                    return true;
                }
            }
            return false;

        } else {

            List<IntelligentRuleExpression> intelligentRuleExpressions = ruleTrigger.getIntelligentRuleExpressionList();
//         判断 表达式里面是否包含 过来报文里面的属性值
            Map<String, Object> mapObj = (Map) JSON.toJSON(data);
            if (ObjectUtils.isEmpty(mapObj)) {
                return false;
            }
            Set<String> propCodes = mapObj.keySet();
//            表达式里面包含推送过来的报文
            boolean expressionContainsData = false;
            for (IntelligentRuleExpression intelligentRuleExpression : intelligentRuleExpressions) {
                if (intelligentRuleExpression.getDeviceAttribute() != null && ObjectUtils.isNotEmpty(intelligentRuleExpression.getDeviceAttribute().getIdentifier())) {
                    if (propCodes.contains(intelligentRuleExpression.getDeviceAttribute().getIdentifier())) {
                        expressionContainsData = true;
                        break;
                    }
                }
            }
            if (!expressionContainsData) {
                return false;
            }

            return intelligentRuleExpressionService.checkMeetExpressions(device, intelligentRuleExpressions);
        }

    }

}



